#include "stdafx.h"

#include "TestudinApplicationBase.h"

#include "..\Resources\ResourceFiles\DevelopmentResourceFile.h"

TestudinApplicationBase *globalApplicationPointer = NULL;

TestudinApplicationBase::TestudinApplicationBase()
{
	globalApplicationPointer = this;

	pResourceCache = NULL;
}

HWND TestudinApplicationBase::GetHwnd()
{ 
	return DXUTGetHWND();
}

//===================================================================
// Win32 Specific Stuff
//
// InitInstance - this checks system resources, creates your window, and launches the game
//
// preprocessor macro setting in the C++ options of the project provides the base macro
// C preprocessor string concatenation takes care of the rest.
//
// GameCodeApp::InitInstance - Chapter 5, page 136
//
//===================================================================

bool TestudinApplicationBase::InitInstance(
	HINSTANCE hInstance, 
	LPWSTR lpCmdLine, 
	HWND hWnd, 
	int screenWidth, 
	int screenHeight)
{
	// Check for existing instance of the same window
	// 
#ifndef _DEBUG
	// Note - it can be really useful to debug network code to have
	// more than one instance of the game up at one time - so
	// feel free to comment these lines in or out as you wish!
	if (!IsOnlyInstance(GetGameTitle()))
		return false;
#endif

	// We don't need a mouse cursor by default, let the game turn it on
	SetCursor( NULL );

	// Check for adequate machine resources.
	bool resourceCheck = false;
	while (!resourceCheck)
	{
		const DWORDLONG physicalRAM = 512 * MEGABYTE;
		const DWORDLONG virtualRAM = 1024 * MEGABYTE;
		const DWORDLONG diskSpace = 10 * MEGABYTE;
		if (!CheckStorage(diskSpace))
			return false;

		const DWORD minCpuSpeed = 1300;			// 1.3Ghz
		DWORD thisCPU = ReadCPUSpeed();
		if (thisCPU < minCpuSpeed)
		{
			TWF_ERROR("GetCPUSpeed reports CPU is too slow for this game.");
			return false;
		}

		resourceCheck = true;
	}

	_hInstance = hInstance;

    // register all events
	RegisterEngineEvents();
    RegisterGameEvents();

	//
	// Initialize the ResCache - Chapter 5, page 141
	//
	//    Note - this is a little different from the book. Here we have a speccial resource ZIP file class, DevelopmentResourceZipFile,
	//    that actually reads directly from the source asset files, rather than the ZIP file. This is MUCH better during development, since
	//    you don't want to have to rebuild the ZIP file every time you make a minor change to an asset.
	//
	
	

	IResourceFile *zipFile = TWF_NEW DevelopmentResourceFile(L"Assets.zip");

	this->pResourceCache = TWF_NEW ResourceCache(50, zipFile);

	if (!this->pResourceCache->Init())
	{
        TWF_ERROR("Failed to initialize resource cache!  Are your paths set up correctly?");
		return false;
	}
	
	/*

	extern shared_ptr<IResourceLoader> CreateWAVResourceLoader();
	extern shared_ptr<IResourceLoader> CreateOGGResourceLoader();
	extern shared_ptr<IResourceLoader> CreateDDSResourceLoader();
	extern shared_ptr<IResourceLoader> CreateJPGResourceLoader();
    extern shared_ptr<IResourceLoader> CreateXmlResourceLoader();
    extern shared_ptr<IResourceLoader> CreateSdkMeshResourceLoader();
    extern shared_ptr<IResourceLoader> CreateScriptResourceLoader();

	// Note - register these in order from least specific to most specific! They get pushed onto a list.
	// RegisterLoader is discussed in Chapter 5, page 142
	_ResCache->RegisterLoader(CreateWAVResourceLoader());
	_ResCache->RegisterLoader(CreateOGGResourceLoader());
	_ResCache->RegisterLoader(CreateDDSResourceLoader());
	_ResCache->RegisterLoader(CreateJPGResourceLoader());
    _ResCache->RegisterLoader(CreateXmlResourceLoader());
	_ResCache->RegisterLoader(CreateSdkMeshResourceLoader());
    _ResCache->RegisterLoader(CreateScriptResourceLoader());

	if(!LoadStrings("English"))
	{
        GCC_ERROR("Failed to load strings");
		return false;
	}

	// Rez up the Lua State manager now, and run the initial script - discussed in Chapter 5, page 144.
    if (!LuaStateManager::Create())
	{
        GCC_ERROR("Failed to initialize Lua");
		return false;
	}

    // Load the preinit file.  This is within braces to create a scope and destroy the resource once it's loaded.  We
    // don't need to do anything with it, we just need to load it.
    {
        Resource resource(SCRIPT_PREINIT_FILE);
        shared_ptr<ResHandle> pResourceHandle = _ResCache->GetHandle(&resource);  // this actually loads the XML file from the zip file
    }

    // Register function exported from C++
    ScriptExports::Register();
    ScriptProcess::RegisterScriptClass();
    BaseScriptComponent::RegisterScriptFunctions();

	// The event manager should be created next so that subsystems can hook in as desired.
	// Discussed in Chapter 5, page 144
	_pEventManager = GCC_NEW EventManager("GameCodeApp Event Mgr", true );
	if (!_pEventManager)
	{
		GCC_ERROR("Failed to create EventManager.");
		return false;
	}*/

	// DXUTInit, DXUTCreateWindow - Chapter 5, page 145	
	DXUTInit( true, true, lpCmdLine, true ); // Parse the command line, handle the default hotkeys, and show msgboxes

	if (hWnd==NULL)
	{
	    DXUTCreateWindow( GetGameTitle(), hInstance, GetIcon() );
	}
	else
	{
	    DXUTSetWindow( hWnd, hWnd, hWnd );
	}

	if (!GetHwnd())
	{
		return FALSE;
	}
	//SetWindowText(GetHwnd(), GetGameTitle());

	// initialize the directory location you can store save game files
	//_tcscpy_s(_saveGameDirectory, GetSaveGameDirectory(GetHwnd(), GetGameAppDirectory()));

	// DXUTCreateDevice - Chapter 5 - page 139
	_screenSize = Point(screenWidth, screenHeight);

	//If you have an older video card that only supports D3D9, comment in the next line, and make sure 
	//the renderer setting in Game\PlayerOptions.xml is set to "Direct3D 9"
	//DXUTCreateDevice( D3D_FEATURE_LEVEL_9_3, true, screenWidth, screenHeight);
	DXUTCreateDevice( D3D_FEATURE_LEVEL_10_1, true, screenWidth, screenHeight);

	/*if (GetRendererImpl() == Renderer_D3D9)
	{
		m_Renderer = shared_ptr<IRenderer>(GCC_NEW D3DRenderer9());
	}
	else if (GetRendererImpl() == Renderer_D3D11)
	{
		m_Renderer = shared_ptr<IRenderer>(GCC_NEW D3DRenderer11());
	}
	m_Renderer->VSetBackgroundColor(255, 20, 20, 200);
	m_Renderer->VOnRestore();*/


	// You usually must have an HWND to initialize your game views...
	//    VCreateGameAndView			- Chapter 5, page 145
	_pGame = CreateGameAndView();
	if (!_pGame)
		return false;

	// now that all the major systems are initialized, preload resources 
	//    Preload calls are discussed in Chapter 5, page 148
	/*_ResCache->Preload("*.ogg", NULL);
	_ResCache->Preload("*.dds", NULL);
	_ResCache->Preload("*.jpg", NULL);

	if ( GameCodeApp::GetRendererImpl() == GameCodeApp::Renderer_D3D11 )
		_ResCache->Preload("*.sdkmesh", NULL);

	CheckForJoystick(GetHwnd());*/

	_bIsRunning = true;

	return TRUE;
}

//----------------------------------------------------------
// Win32 Specific Message Handlers
//
// WndProc - the main message handler for the window class
//
// OnNcCreate - this is where you can set window data before it is created
// OnMove - called whenever the window moves; used to update members of g_App
// OnDeviceChange - called whenever you eject the CD-ROM.
// OnDisplayChange - called whenever the user changes the desktop settings
// OnPowerBroadcast - called whenever a power message forces a shutdown
// OnActivate - called whenever windows on the desktop change focus.
//
// Note: pUserContext added to comply with DirectX 9c - June 2005 Update
//
LRESULT CALLBACK TestudinApplicationBase::MsgProc( 
	HWND hWnd, 
	UINT uMsg, 
	WPARAM wParam, 
	LPARAM lParam, 
	bool* pbNoFurtherProcessing, 
	void* pUserContext )
{
    // Always allow dialog resource manager calls to handle global messages
    // so GUI state is updated correctly
    
	/*
	*pbNoFurtherProcessing = D3DRenderer::g_DialogResourceManager.MsgProc( hWnd, uMsg, wParam, lParam );
    if( *pbNoFurtherProcessing )
        return 0;

	LRESULT result = 0;

	switch (uMsg) 
	{
		case WM_POWERBROADCAST:
		{
			int event = (int)wParam;
			result = g_pApp->OnPowerBroadcast(event);
			break;
		}

		case WM_DISPLAYCHANGE:
		{
			int colorDepth = (int)wParam;
			int width = (int)(short) LOWORD(lParam);
			int height = (int)(short) HIWORD(lParam);

			result = g_pApp->OnDisplayChange(colorDepth, width, height);
			break;
		}

		case WM_SYSCOMMAND: 
		{
			result = g_pApp->OnSysCommand(wParam, lParam);
			if (result)
			{
				*pbNoFurtherProcessing = true;
			}
			break;
		}

		case WM_SYSKEYDOWN:
		{
			if (wParam == VK_RETURN)
			{
				*pbNoFurtherProcessing = true;
				return g_pApp->OnAltEnter();
			}
			return DefWindowProc(hWnd, uMsg, wParam, lParam);
		}


		case WM_CLOSE:
		{
			// DXUT apps choose ESC key as a default exit command.
			// GameCode4 doesn't like this so we disable it by checking 
			// the m_bQuitting bool, and if we're not really quitting
			// set the "no further processing" parameter to true.
			if (g_pApp->m_bQuitting)
			{
				result = g_pApp->OnClose();
			}
			else
			{
				*pbNoFurtherProcessing = true;
			}
			break;
		}


		case WM_KEYDOWN:
        case WM_KEYUP:
		case WM_CHAR:
		case WM_MOUSEMOVE:
		case WM_LBUTTONDOWN:
		case WM_LBUTTONUP:
		case WM_RBUTTONDOWN:
		case WM_RBUTTONUP:
		case MM_JOY1BUTTONDOWN:
		case MM_JOY1BUTTONUP:
		case MM_JOY1MOVE:
		case MM_JOY1ZMOVE:
		case MM_JOY2BUTTONDOWN:
		case MM_JOY2BUTTONUP:
		case MM_JOY2MOVE:
		case MM_JOY2ZMOVE:
		{
			//
			// See Chapter 10, page 278 for more explanation of this code.
			//
			if (g_pApp->m_pGame)
			{
				BaseGameLogic *pGame = g_pApp->m_pGame;
				// Note the reverse order! User input is grabbed first from the view that is on top, 
				// which is the last one in the list.
				AppMsg msg;
				msg.m_hWnd = hWnd;
				msg.m_uMsg = uMsg;
				msg.m_wParam = wParam;
				msg.m_lParam = lParam;
				for(GameViewList::reverse_iterator i=pGame->m_gameViews.rbegin(); i!=pGame->m_gameViews.rend(); ++i)
				{
					if ( (*i)->VOnMsgProc( msg ) )
					{
						result = true;
						break;				// WARNING! This breaks out of the for loop.
					}
				}
			}
			break;
		}

		/**********************
		WARNING!!!!! You MIGHT think you need this, but if you use the DirectX
		Framework the DefWindowProc is called for you....

		default:
			return DefWindowProc(hWnd, message, wParam, lParam);

		***********************/
	//}
	
	//return result;

	return 0;
}

//--------------------------------------------------------------------------------------
// Reject any D3D11 devices that aren't acceptable by returning false
//--------------------------------------------------------------------------------------
bool CALLBACK TestudinApplicationBase::IsD3D11DeviceAcceptable( 
	const CD3D11EnumAdapterInfo *AdapterInfo, 
	UINT Output, 
	const CD3D11EnumDeviceInfo *DeviceInfo,
    DXGI_FORMAT BackBufferFormat, 
	bool bWindowed, 
	void* pUserContext )
{
    return true;
}


//--------------------------------------------------------------------------------------
// Create any D3D11 resources that aren't dependant on the back buffer
//--------------------------------------------------------------------------------------
HRESULT CALLBACK TestudinApplicationBase::OnD3D11CreateDevice( 
	ID3D11Device* pd3dDevice, 
	const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc,
    void* pUserContext )
{
    HRESULT hr;

    ID3D11DeviceContext* pd3dImmediateContext = DXUTGetD3D11DeviceContext();
    
	/*
	V_RETURN( D3DRenderer::g_DialogResourceManager.OnD3D11CreateDevice( pd3dDevice, pd3dImmediateContext ) );
	*/

    return S_OK;
}


//--------------------------------------------------------------------------------------
// Create any D3D11 resources that depend on the back buffer
//--------------------------------------------------------------------------------------
HRESULT CALLBACK TestudinApplicationBase::OnD3D11ResizedSwapChain( 
	ID3D11Device* pd3dDevice, 
	IDXGISwapChain* pSwapChain,
    const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc, 
	void* pUserContext )
{
    HRESULT hr;

	/*
    V_RETURN( D3DRenderer::g_DialogResourceManager.OnD3D11ResizedSwapChain( pd3dDevice, pBackBufferSurfaceDesc ) );

	if (g_pApp->m_pGame)
	{
		BaseGameLogic *pGame = g_pApp->m_pGame;
		for(GameViewList::iterator i=pGame->m_gameViews.begin(); i!=pGame->m_gameViews.end(); ++i)
		{
			(*i)->VOnRestore();
		}
	}
	*/

    return S_OK;
}


//--------------------------------------------------------------------------------------
// Render the scene using the D3D11 device
//--------------------------------------------------------------------------------------
void CALLBACK TestudinApplicationBase::OnD3D11FrameRender( 
	ID3D11Device* pd3dDevice, 
	ID3D11DeviceContext* pd3dImmediateContext, 
	double fTime,
    float fElapsedTime, 
	void* pUserContext )
{
	/*

	BaseGameLogic *pGame = g_pApp->m_pGame;

	for(GameViewList::iterator i=pGame->m_gameViews.begin(),
		end=pGame->m_gameViews.end(); i!=end; ++i)
	{
		(*i)->VOnRender(fTime, fElapsedTime);
	}

	g_pApp->m_pGame->VRenderDiagnostics();

	*/
}


//--------------------------------------------------------------------------------------
// Release D3D11 resources created in OnD3D11ResizedSwapChain 
//--------------------------------------------------------------------------------------
void CALLBACK TestudinApplicationBase::OnD3D11ReleasingSwapChain( void* pUserContext )
{
	/*
    D3DRenderer::g_DialogResourceManager.OnD3D11ReleasingSwapChain();
	*/
}


//--------------------------------------------------------------------------------------
// Release D3D11 resources created in OnD3D11CreateDevice 
//--------------------------------------------------------------------------------------
void CALLBACK TestudinApplicationBase::OnD3D11DestroyDevice( void* pUserContext )
{
	/*
    if (g_pApp->m_Renderer)  // [rez] Fix for multi-monitor issue when target monitor is portrait; posted by Kl1X
	    g_pApp->m_Renderer->VShutdown();
    D3DRenderer::g_DialogResourceManager.OnD3D11DestroyDevice();
	g_pApp->m_Renderer = shared_ptr<IRenderer>(NULL);
	*/
}


//--------------------------------------------------------------------------------------
// Called right before creating a D3D9 or D3D11 device, allowing the app to modify the device settings as needed
//--------------------------------------------------------------------------------------
bool CALLBACK TestudinApplicationBase::ModifyDeviceSettings( 
	DXUTDeviceSettings* pDeviceSettings, 
	void* pUserContext )
{
    if( pDeviceSettings->ver == DXUT_D3D9_DEVICE )
    {
        IDirect3D9* pD3D = DXUTGetD3D9Object();
        D3DCAPS9 Caps;
        pD3D->GetDeviceCaps( pDeviceSettings->d3d9.AdapterOrdinal, pDeviceSettings->d3d9.DeviceType, &Caps );

        // If device doesn't support HW T&L or doesn't support 1.1 vertex shaders in HW 
        // then switch to SWVP.
        if( ( Caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT ) == 0 ||
            Caps.VertexShaderVersion < D3DVS_VERSION( 1, 1 ) )
        {
            pDeviceSettings->d3d9.BehaviorFlags = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
        }

        // Debugging vertex shaders requires either REF or software vertex processing 
        // and debugging pixel shaders requires REF.  
#ifdef DEBUG_VS
        if( pDeviceSettings->d3d9.DeviceType != D3DDEVTYPE_REF )
        {
            pDeviceSettings->d3d9.BehaviorFlags &= ~D3DCREATE_HARDWARE_VERTEXPROCESSING;
            pDeviceSettings->d3d9.BehaviorFlags &= ~D3DCREATE_PUREDEVICE;
            pDeviceSettings->d3d9.BehaviorFlags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING;
        }
#endif
#ifdef DEBUG_PS
        pDeviceSettings->d3d9.DeviceType = D3DDEVTYPE_REF;
#endif
    }

    // For the first device created if its a REF device, optionally display a warning dialog box
    static bool s_bFirstTime = true;
    if( s_bFirstTime )
    {
        s_bFirstTime = false;
        if( ( DXUT_D3D9_DEVICE == pDeviceSettings->ver && pDeviceSettings->d3d9.DeviceType == D3DDEVTYPE_REF ) ||
            ( DXUT_D3D11_DEVICE == pDeviceSettings->ver &&
            pDeviceSettings->d3d11.DriverType == D3D_DRIVER_TYPE_REFERENCE ) )
        {
            DXUTDisplaySwitchingToREFWarning( pDeviceSettings->ver );
        }

    }

    return true;
}





//--------------------------------------------------------------------------------------
// This callback function will be called once at the beginning of every frame. This is the
// best location for your application to handle updates to the scene, but is not 
// intended to contain actual rendering calls, which should instead be placed in the 
// OnFrameRender callback.  
//
// See Game Coding Complete - 4th Edition - Chapter X, page Y
//--------------------------------------------------------------------------------------
void CALLBACK TestudinApplicationBase::OnUpdateGame( 
	double fTime, 
	float fElapsedTime, 
	void* pUserContext  )
{
	int i = 0;

	/*

	if (g_pApp->HasModalDialog())
	{	
		// don't update the game if a modal dialog is up.
		return;
	}

	if (g_pApp->m_bQuitting)
	{
		PostMessage(g_pApp->GetHwnd(), WM_CLOSE, 0, 0);
	}

	if (g_pApp->m_pGame)
	{
        IEventManager::Get()->VUpdate(20); // allow event queue to process for up to 20 ms

		if (g_pApp->m_pBaseSocketManager)
			g_pApp->m_pBaseSocketManager->DoSelect(0);	// pause 0 microseconds

		g_pApp->m_pGame->VOnUpdate(float(fTime), fElapsedTime);
	}

	*/
}

void TestudinApplicationBase::RegisterEngineEvents(void)
{
	/*
    REGISTER_EVENT(EvtData_Environment_Loaded);
    REGISTER_EVENT(EvtData_New_Actor);
    REGISTER_EVENT(EvtData_Move_Actor);
    REGISTER_EVENT(EvtData_Destroy_Actor);
	REGISTER_EVENT(EvtData_Request_New_Actor);
	REGISTER_EVENT(EvtData_Network_Player_Actor_Assignment);
	*/
}

bool TestudinApplicationBase::LoadGame()
{
	return true;
}

bool TestudinApplicationBase::CheckStorage(const DWORDLONG diskSpaceNeeded)
{
	return true;
}

DWORD TestudinApplicationBase::ReadCPUSpeed()
{
	return 2600;
}